// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <pollux/tpch/gen/db_gen_iterator.h>

#include <melon/singleton.h>
#include <pollux/tpch/gen/dbgen/dbgen_gunk.hpp>
#include <pollux/common/base/exceptions.h>

namespace kumo::pollux::tpch {
    namespace {
        using namespace dbgen;

        // DBGenBackend is a singleton that controls access to the DBGEN C functions,
        // and ensures that the required structures are properly initialized and
        // destructed.
        //
        // Only acquire instances of this class using melon::Singleton.
        class DBGenBackend {
        public:
            DBGenBackend() {
                // load_dists()/cleanup_dists() need to be called to ensure the global
                // structures required by dbgen are populated.
                DBGenContext dbgenCtx;
                load_dists(
                    turbo::get_flag(FLAGS_pollux_tpch_text_pool_size_mb) * 1024 * 1024,
                    &dbgenCtx); // 300 MB buffer size for text generation.

                // Initialize global dbgen buffers required to generate data.
                init_build_buffers();
            }

            ~DBGenBackend() {
                cleanup_dists();
            }
        };

        // Make the object above a singleton.
        static melon::Singleton<DBGenBackend> DBGenBackendSingleton;
    } // namespace

    DBGenIterator::DBGenIterator(double scaleFactor) {
        auto dbgenBackend = DBGenBackendSingleton.try_get();
        POLLUX_CHECK_NOT_NULL(dbgenBackend, "Unable to initialize dbgen's dbgunk.");
        POLLUX_CHECK_GE(scaleFactor, 0, "Tpch scale factor must be non-negative");
        if (scaleFactor < MIN_SCALE && scaleFactor > 0) {
            dbgenCtx_.scale_factor = 1;
        } else {
            dbgenCtx_.scale_factor = static_cast<long>(scaleFactor);
        }
    }

    void DBGenIterator::initNation(size_t offset) {
        sd_nation(NATION, offset, &dbgenCtx_);
    }

    void DBGenIterator::initRegion(size_t offset) {
        sd_region(REGION, offset, &dbgenCtx_);
    }

    void DBGenIterator::initOrder(size_t offset) {
        sd_order(ORDER, offset, &dbgenCtx_);
        sd_line(LINE, offset, &dbgenCtx_);
    }

    void DBGenIterator::initSupplier(size_t offset) {
        sd_supp(SUPP, offset, &dbgenCtx_);
    }

    void DBGenIterator::initPart(size_t offset) {
        sd_part(PART, offset, &dbgenCtx_);
        sd_psupp(PSUPP, offset, &dbgenCtx_);
    }

    void DBGenIterator::initCustomer(size_t offset) {
        sd_cust(CUST, offset, &dbgenCtx_);
    }

    void DBGenIterator::genNation(size_t index, code_t &code) {
        row_start(NATION, &dbgenCtx_);
        mk_nation(index, &code, &dbgenCtx_);
        row_stop_h(NATION, &dbgenCtx_);
    }

    void DBGenIterator::genRegion(size_t index, code_t &code) {
        row_start(REGION, &dbgenCtx_);
        mk_region(index, &code, &dbgenCtx_);
        row_stop_h(REGION, &dbgenCtx_);
    }

    void DBGenIterator::genOrder(size_t index, order_t &order) {
        row_start(ORDER, &dbgenCtx_);
        mk_order(index, &order, &dbgenCtx_, /*update-num=*/0);
        row_stop_h(ORDER, &dbgenCtx_);
    }

    void DBGenIterator::genSupplier(size_t index, supplier_t &supplier) {
        row_start(SUPP, &dbgenCtx_);
        mk_supp(index, &supplier, &dbgenCtx_);
        row_stop_h(SUPP, &dbgenCtx_);
    }

    void DBGenIterator::genPart(size_t index, part_t &part) {
        row_start(PART, &dbgenCtx_);
        mk_part(index, &part, &dbgenCtx_);
        row_stop_h(PART, &dbgenCtx_);
    }

    void DBGenIterator::genCustomer(size_t index, customer_t &customer) {
        row_start(CUST, &dbgenCtx_);
        mk_cust(index, &customer, &dbgenCtx_);
        row_stop_h(CUST, &dbgenCtx_);
    }
} // namespace kumo::pollux::tpch
